Skip to main content

Convertir una aplicación React.js con Vite a una aplicación de escritorio.

Descripción

Hasta el momento, el caso de uso que provoca que una aplicación desarrollada con React.js se convierta en un ejecutable es que cliente no cuenta con servicio de internet en el lugar donde se va a utilizar la aplicación, por lo tanto, se le proporciona un ejecutable que permita correr la aplicación de forma nativa. Esto se logra a través de Electron.

Para este ejemplo, el proyecto cuenta con las siguientes dependencias:

"react": "^18.2.0",
"electron-is-dev": "^3.0.1",
"react-router-dom": "^6.21.3",
"cross-env": "^7.0.3",
"electron": "^33.2.0",
"electron-builder": "^25.1.8",
"wait-on": "^8.0.1"

Nota: El ejemplo es utilizando un proyecto creado con Vite Nota: El ejemplo fue desarrollado con una aplicación CMS y con una aplicación con Three.js

Paso 1: Instalaciones necesarias

Las siguientes son instalaciones necesarias para transformar el proyecto

npm i electron-is-dev
npm i -D concurrently cross-env electron electron-builder wait-on

Paso 2:

Crear el archivo de configuración de Electron.

El nombre del archivo es importante y comúnmente se llama main.js, debe de ir en la raíz del proyecto (afuera de src). En caso de ocupar otro nombre, se debe tomar en cuenta para pasos posteriores.

import { app, BrowserWindow } from "electron";
import { join, dirname } from "path";
import { fileURLToPath } from "url";

let mainWindow;

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

function createWindow() {
// Detecta si estamos en modo desarrollo
const isDev = process.env.NODE_ENV === "development";

// Crea la ventana principal
mainWindow = new BrowserWindow({
width: 1200,
height: 1000,
title: "Bimbo MTY", // **Este nombre aparecerá en la barra de tareas**
autoHideMenuBar: true,
icon: join(app.getAppPath(), "public/icon.icns"), // **Imagen icono de la aplicación (es icns porque se desarrollo en mac, para windows es ico)**

webPreferences: {
preload: join(__dirname, "preload.js"), // Ruta al archivo preload.js (permite exportar funciones de Electron, por el momento no se tiene un uso)
},
});

// Maximiza la ventana al abrir
mainWindow.maximize();

// Carga la URL según el entorno
const url = isDev
? "http://localhost:5173" // Asegúrate de que el servidor de desarrollo esté activo
: `file://${join(__dirname, "dist", "index.html")}`; // Archivo generado por Vite en modo producción

mainWindow.loadURL(url);

// Limpia la referencia a la ventana cuando se cierra
mainWindow.on("closed", () => {
mainWindow = null;
});
}

// Espera a que la app esté lista para crear la ventana
app.whenReady().then(() => {
createWindow();

// En macOS, vuelve a abrir la ventana si no hay ninguna activa
app.on("activate", () => {
// En macOS, vuelve a abrir la ventana si todas están cerradas
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});

// Salir de la aplicación cuando todas las ventanas están cerradas, excepto en macOS
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit();
}
});

Paso 3 (Opcional): Agregar el preload.js

Por el momento no se ha requerido el uso del archivo preload, pero es una buen práctica agregarlo con el objetivo de usar funciones de Electron. Debe de ir en la raíz del proyecto, mismo nivel que el archivo del paso

import { contextBridge } from "electron";

contextBridge.exposeInMainWorld("myApi", {
// Aquí puedes exponer funciones del lado de Electron al frontend
});

Paso 4: Crear carpeta de íconos

Dentro de la carpeta public, estarán los íconos para los diferentes sistemas operativos:

  1. Para Windows: archivo .ico.
  2. Para Mac: archivo .icns.
  3. Para Linux: archivo .png.

Es importante que las imágenes tengan una resolución de 256 x 256 píxeles.

Paso 5: Modificar package.json

Agrega los siguientes scripts y configuraciones en tu archivo package.json:

  1. Información del autor y descripción del proyecto:
"author": {
"name": "Inmersys web developer",
"email": "inmersys.developer@gmail.com"
},
"description": "CMS for VRTraining created with React 18 and built with Electron",
"homepage": "./",
  1. Agregar el archivo de configuración de Electron. El nombre del archivo propuesto como main.js debe de concidir en este paso
"main": "main.js",
  1. Comandos para ejecutar el proyecto en modo desarrollo y producción:
"scripts": {
"electron:dev": "cross-env NODE_ENV=development concurrently \"vite\" \"wait-on http://localhost:5173 && electron .\"", // En desarrollo se puede apreciar una versión cercana a la final.
"electron:build": "vite build && electron-builder",
"electron:build:mac": "vite build && electron-builder --mac", // Únicamente genera el ejecutable para mac
"electron:build:win": "vite build && electron-builder --win"// Únicamente genera el ejecutable para windows
}
  1. Configuración de los archivos de producción
"build": {
"productName": "Bimbo MTY",
"copyright": "Copyright © 2022 Inmersys",
"electronVersion": "33.2.0",
"appId": "com.bimbo.electron",
"directories": {
"buildResources": "public",
"output": "dist_electron" // Carpeta que se generará al finalizar el compilado de Electron
},
"files": [
"dist/**/*", // Este es el único necesario, ya que antes del compilado de electron, se crea el build/dist de react
"main.js",
"preload.js"
],
"win": {
"target": "nsis",
"icon": "public/logo_marinela.png"
},
"mac": {
"target": "dmg",
"icon": "public/icon.icns",
"identity": null
},
"linux": {
"icon": "public/icon.png",
"target": "deb"
}
}

Paso 8: Modificar rutas en el proyecto

Cambia BrowserRouter por HashRouter en las rutas de tu proyecto provenientes de la librería react-router-dom. Si se esta usando v6 o superior se debe modificar la forma de agregar hijos con una configuración simialr a la v5.

import { HashRouter } from "react-router-dom";
<HashRouter>
<main className="fixed top-0 w-full h-full font-montserrat">
{/* Aquí define tus rutas */}
<Routes>
{/* Rutas privadas */}
<Route path={routerPrivate.path} element={routerPrivate.element}>
<Route index element={routerPrivate.children[0].element} />
</Route>

{/* Ruta de error */}
<Route path="*" element={<NotFound />} />
</Routes>
<Versioner />
</main>
</HashRouter>

Paso 9: Generar instalables

Para obtener los instalables que usaremos en nuestros diferentes sistemas operativos, ejecuta uno de los siguientes comandos:

npm run electron:build:mac
npm run electron:build:win

Paso 10: Ubicar los instalables

Se generarán dos carpetas, build y dist_electron. Dentro de la carpeta dist_electron encontrarás el instalable del sistema operativo que creaste.

  1. img

Paso 11: Vista de la aplicación de escritorio

  1. img

Paso 12: Firma de la aplicación de escritorio

Es importante firmar la aplicación para asegurar su autenticidad y seguridad.

Nota: Es importante seguir investigando acerca de responsividad, funciones nativas y limitaciones.